/*  README:
*       Algoritmo de MonteCarlo(Sequencial)
*
*           Algoritmo que converge aleatoriamente com precisao de 10.000 casas decimais,
*           portanto temos 25 iteracoes para seu calculo na main;
*           para alterar a quantidade de iteracoes deve-se alterar o iterador de 25 para o desejado na funcao;
*           o tempo eh fornecido em milisegundos,; defina a precisao na variavel "prec"
*
*/

#include<stdio.h>
#include<time.h>
#include<gmp.h>
#include<time.h>

void monteCarlo(int n){
    int i;
    int pin = 0;
    char string[FILENAME_MAX];

    //Declara as variaveis do tipo float de alta precisao
    mpf_t PIn;
    mpf_t PTotal;
    mpf_t Pi;
    mpf_t check;
    mpf_t rand_NumX;
    mpf_t rand_NumY;

    //Inicializa as variaveis com a precisao declarada anteriormente
    mpf_init(PIn);
    mpf_init(PTotal);
    mpf_init(Pi);
    mpf_init(check);
    mpf_init(rand_NumX);
    mpf_init(rand_NumY);

    //Gera o algoritmo que sera utilizado para a geracao dos numeros randomicos entre 0 e 1 do tipo float de alta precisao
    gmp_randstate_t r_state;
    gmp_randinit_mt (r_state);
    gmp_randseed_ui(r_state, time(NULL));

    for(i = 1 ; i < n ; i++){
        //Armazena em rand_NumX e rand_NumY os numeros randomicos atraves da variavel r_state iniciada anteriormente
        mpf_urandomb(rand_NumX,r_state,10000);
        mpf_urandomb(rand_NumY,r_state,10000);

        //Armazena em check o resultado de (x^2)+(y^2)
        mpf_pow_ui(rand_NumX,rand_NumX,2);
        mpf_pow_ui(rand_NumY,rand_NumY,2);
        mpf_add(check, rand_NumX, rand_NumY);

        //Verifica se check eh menor ou igual a 1; Caso afirmativo, incrementa a variavel pin que contem a quantidade de pontos dentro do circulo
        mpf_set_str(rand_NumX, "1", 10);
        if(mpf_cmp(check, rand_NumX) < 0)
            pin++;
    }

    //Converte os valores inteiros dos pontos internos e do total para float de alta precisao
    sprintf(string, "%d", pin);
    mpf_set_str(PIn, string, 10);
    sprintf(string, "%d", n);
    mpf_set_str(PTotal, string, 10);

    gmp_printf("PIn: %Ff \n",PIn);
    gmp_printf("PTotal: %Ff \n",PTotal);

    //Multiplicao por 4 para chegar no valor aproximado de pi
    mpf_div(Pi, PIn, PTotal);
    mpf_set_str(PTotal, "4", 10);
    mpf_mul(Pi, Pi, PTotal);

    gmp_printf("%.50Ff\n",Pi);

    gmp_randclear(r_state);
    mpf_clears(PIn, PTotal, Pi, check, rand_NumX, rand_NumY, NULL);
}

int main(){
    clock_t tInicio, tFim;
    tInicio = clock();

    //Define a precisao juntamente com a quantidade de pontos dentro do circulo
    mpf_set_default_prec(10000);

    //Funcao com o parametro do total de pontos a serem testados
    monteCarlo(999999);
    tFim = clock();
    printf("\nTempo Decorrido:%ld ms\n\n", (tFim-tInicio)/(CLOCKS_PER_SEC/1000));

    return 0;
}
